import math

import Constants
import Materials
from Constants import CHUNK_SIZE
from OpenGL.GL import *  # 导入OpenGL库，用于进行3D渲染
from OpenGL.GLUT import *  # 导入GLUT库，用于创建OpenGL上下文（这里没有实际使用）
from OpenGL.GLU import *  # 导入GLU库，用于进行高级3D图形操作
import numpy as np
import pyrr
import Geometry
from World import BlockRegistry, BlockKinds
import asyncio
import Utils
from Utils import JitFunc

BLOCK_DATA_TEXTURE_ID = []
BLOCK_DATA_TRANSPARENT = []
BLOCK_DATA_SMOOTHNESS = []
BLOCK_DATA_SHAPE = []
for block_id, block in BlockRegistry.items():
    BLOCK_DATA_TEXTURE_ID.append(block.texture_id)
    BLOCK_DATA_TRANSPARENT.append(block.transparent)
    BLOCK_DATA_SMOOTHNESS.append(block.smoothness)
    BLOCK_DATA_SHAPE.append(block.shape)
BLOCK_DATA_TRANSPARENT = np.array(BLOCK_DATA_TRANSPARENT, dtype=np.bool_)
BLOCK_DATA_TEXTURE_ID = np.array(BLOCK_DATA_TEXTURE_ID, dtype=np.float32)


class ChunkMesh:
    def __init__(self, chunk_data):
        self.chunk_data = chunk_data
        self.vao = glGenVertexArrays(1)
        self.vbo = glGenBuffers(1)
        self.ebo = glGenBuffers(1)
        self.index = 0
        self.vertices = np.array([0], dtype=np.float32)
        self.indices = np.array([0], dtype=np.int32)
        self.has_generated = False
        self.has_initialized = False
        self.need_update = False
        self.material = self.chunk_data.world.opaque_material
        self.model = None
        self.indices_len = 0

    async def generate_data_delay(self, delay):
        await asyncio.sleep(delay)
        self.generate_data()

    def generate_data_callback(self, result):
        self.vertices, self.indices = result
        self.has_generated, self.need_update = True, self.has_initialized

    def update_mesh(self):
        glBindVertexArray(self.vao)
        glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
        glBufferData(GL_ARRAY_BUFFER, self.vertices.nbytes, self.vertices, GL_DYNAMIC_DRAW)
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.ebo)
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, self.indices.nbytes, self.indices, GL_DYNAMIC_DRAW)
        # 位置属性
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 10 * self.vertices.itemsize, ctypes.c_void_p(0))
        glEnableVertexAttribArray(0)
        # 纹理属性
        glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 10 * self.vertices.itemsize,
                              ctypes.c_void_p(3 * self.vertices.itemsize))
        glEnableVertexAttribArray(1)

        # 法向量属性
        glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 10 * self.vertices.itemsize,
                              ctypes.c_void_p(5 * self.vertices.itemsize))
        glEnableVertexAttribArray(2)

        # 环境遮蔽强度属性
        glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, 10 * self.vertices.itemsize,
                              ctypes.c_void_p(8 * self.vertices.itemsize))
        glEnableVertexAttribArray(3)

        # 光滑度属性
        glVertexAttribPointer(4, 1, GL_FLOAT, GL_FALSE, 10 * self.vertices.itemsize,
                              ctypes.c_void_p(9 * self.vertices.itemsize))
        glEnableVertexAttribArray(4)

        self.model = pyrr.matrix44.create_from_translation(
            np.array([self.chunk_data.position[0], 0, self.chunk_data.position[1]]))

        self.indices_len = len(self.indices)

    def generate_data(self):
        forward_key = (self.chunk_data.position[0], self.chunk_data.position[1] + CHUNK_SIZE)
        backward_key = (self.chunk_data.position[0], self.chunk_data.position[1] - CHUNK_SIZE)
        left_key = (self.chunk_data.position[0] - CHUNK_SIZE, self.chunk_data.position[1])
        right_key = (self.chunk_data.position[0] + CHUNK_SIZE, self.chunk_data.position[1])
        forward_left_key = (self.chunk_data.position[0] - CHUNK_SIZE, self.chunk_data.position[1] + CHUNK_SIZE)
        forward_right_key = (self.chunk_data.position[0] + CHUNK_SIZE, self.chunk_data.position[1] + CHUNK_SIZE)
        backward_left_key = (self.chunk_data.position[0] - CHUNK_SIZE, self.chunk_data.position[1] - CHUNK_SIZE)
        backward_right_key = (self.chunk_data.position[0] + CHUNK_SIZE, self.chunk_data.position[1] - CHUNK_SIZE)
        (forward_blocks, backward_blocks, left_blocks, right_blocks,
         forward_left_blocks, forward_right_blocks, backward_left_blocks,
         backward_right_blocks) = None, None, None, None, None, None, None, None

        if forward_key in self.chunk_data.world.chunks:
            forward_blocks = self.chunk_data.world.chunks[forward_key].blocks[:, :, 0]
        if backward_key in self.chunk_data.world.chunks:
            backward_blocks = self.chunk_data.world.chunks[backward_key].blocks[:, :, CHUNK_SIZE - 1]
        if left_key in self.chunk_data.world.chunks:
            left_blocks = self.chunk_data.world.chunks[left_key].blocks[CHUNK_SIZE - 1, :, :]
        if right_key in self.chunk_data.world.chunks:
            right_blocks = self.chunk_data.world.chunks[right_key].blocks[0, :, :]
        if forward_left_key in self.chunk_data.world.chunks:
            forward_left_blocks = self.chunk_data.world.chunks[forward_left_key].blocks[CHUNK_SIZE - 1, :, 0]
        if forward_right_key in self.chunk_data.world.chunks:
            forward_right_blocks = self.chunk_data.world.chunks[forward_right_key].blocks[0, :, 0]
        if backward_left_key in self.chunk_data.world.chunks:
            backward_left_blocks = self.chunk_data.world.chunks[backward_left_key].blocks[CHUNK_SIZE - 1, :,
                                   CHUNK_SIZE - 1]
        if backward_right_key in self.chunk_data.world.chunks:
            backward_right_blocks = self.chunk_data.world.chunks[backward_right_key].blocks[0, :, CHUNK_SIZE - 1]

        self.chunk_data.world.pool.apply_async(JitFunc.generate_data_jit,
                                               args=(
                                                   self.chunk_data.blocks,
                                                   forward_blocks,
                                                   backward_blocks,
                                                   left_blocks,
                                                   right_blocks,
                                                   forward_left_blocks,
                                                   forward_right_blocks,
                                                   backward_left_blocks,
                                                   backward_right_blocks,
                                                   BLOCK_DATA_TEXTURE_ID,
                                                   BLOCK_DATA_TRANSPARENT,
                                                   BLOCK_DATA_SMOOTHNESS,
                                                   BLOCK_DATA_SHAPE
                                               ),
                                               callback=self.generate_data_callback)

    def draw(self):
        if not self.has_generated:
            return
        if not self.has_initialized:
            self.chunk_data.world.draw_count += 1
            self.update_mesh()
            self.has_initialized = True
        if self.need_update:
            self.update_mesh()
            self.need_update = False
        glBindVertexArray(self.vao)
        glUniformMatrix4fv(self.material.model_loc, 1, GL_FALSE, self.model)
        glDrawElements(GL_TRIANGLES, self.indices_len, GL_UNSIGNED_INT, None)
        return

    def __del__(self):
        try:
            glDeleteVertexArrays(1, [self.vao])
            glDeleteBuffers(1, [self.vbo, self.ebo])
        except Exception as e:
            print("OpenGL因程序退出而造成删除函数错误！")


class SkyboxMesh:
    def __init__(self, material, camera):
        self.vao = glGenVertexArrays(1)
        self.vbo = glGenBuffers(1)
        self.ebo = glGenBuffers(1)
        self.vertices = Geometry.skybox_vertices
        self.indices = Geometry.skybox_indices
        self.material = material
        self.camera = camera
        self.model = None
        self.has_initialized = False

    def update_mesh(self):
        glBindVertexArray(self.vao)

        glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
        glBufferData(GL_ARRAY_BUFFER, self.vertices.nbytes, self.vertices, GL_STATIC_DRAW)

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.ebo)
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, self.indices.nbytes, self.indices, GL_STATIC_DRAW)

        # 位置属性
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * self.vertices.itemsize, ctypes.c_void_p(0))
        glEnableVertexAttribArray(0)

        # 纹理属性
        glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * self.vertices.itemsize,
                              ctypes.c_void_p(3 * self.vertices.itemsize))
        glEnableVertexAttribArray(1)

        # 绑定纹理
        glActiveTexture(GL_TEXTURE0)
        glBindTexture(GL_TEXTURE_2D, self.material.sun_texture)
        # 设置uniform
        glUniform1i(glGetUniformLocation(self.material.shader, "sunTexture"), 0)

    def draw(self):
        if not self.has_initialized:
            self.update_mesh()
            self.has_initialized = True
        glUseProgram(self.material.shader)
        glBindVertexArray(self.vao)

        self.model = pyrr.matrix44.create_from_translation(
            self.camera.transform.position)
        glUniformMatrix4fv(self.material.model_loc, 1, GL_FALSE, self.model)

        # 绑定纹理
        # glBindTexture(GL_TEXTURE_2D, self.material.sun_texture)

        # # 设置uniform
        # glUniform1i(glGetUniformLocation(self.material.shader, "sunTexture"), 0)

        glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, None)


class SkyboxMainMenuMesh:
    def __init__(self, material, camera, game_clock):
        self.vao = glGenVertexArrays(1)
        self.vbo = glGenBuffers(1)
        self.ebo = glGenBuffers(1)
        self.vertices = Geometry.skybox_vertices
        self.indices = Geometry.skybox_indices
        self.material = material
        self.camera = camera
        self.game_clock = game_clock
        self.model = None
        self.has_initialized = False

        self.rot_angle = 0

    def update_mesh(self):
        glBindVertexArray(self.vao)

        glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
        glBufferData(GL_ARRAY_BUFFER, self.vertices.nbytes, self.vertices, GL_STATIC_DRAW)

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.ebo)
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, self.indices.nbytes, self.indices, GL_STATIC_DRAW)

        # 位置属性
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * self.vertices.itemsize, ctypes.c_void_p(0))
        glEnableVertexAttribArray(0)

        # 纹理属性
        glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * self.vertices.itemsize,
                              ctypes.c_void_p(3 * self.vertices.itemsize))
        glEnableVertexAttribArray(1)
        glBindTexture(GL_TEXTURE_CUBE_MAP, self.material.texture)

    def draw(self):
        if not self.has_initialized:
            self.update_mesh()
            self.has_initialized = True
        glUseProgram(self.material.shader)
        glBindVertexArray(self.vao)

        self.model = pyrr.matrix44.create_from_translation(
            self.camera.transform.position)
        self.rot_angle -= self.game_clock.delta_time * 0.1
        rot_matrix = pyrr.matrix44.create_from_y_rotation(self.rot_angle)
        self.model = pyrr.matrix44.multiply(rot_matrix, self.model)
        glUniformMatrix4fv(self.material.model_loc, 1, GL_FALSE, self.model)

        glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, None)


class CloudsMesh:
    def __init__(self, game_clock, material, camera, world=None):
        self.vao = glGenVertexArrays(1)
        self.vbo = glGenBuffers(1)
        self.ebo = glGenBuffers(1)
        self.vertices = np.array([], dtype=np.float32)
        self.indices = np.array([], dtype=np.uint32)
        self.game_clock = game_clock
        self.material = material
        self.camera = camera
        self.world = world
        self.cur_offset = 0
        self.has_initialized = False

    def update_mesh(self):
        glBindVertexArray(self.vao)

        glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
        glBufferData(GL_ARRAY_BUFFER, self.vertices.nbytes, self.vertices, GL_STATIC_DRAW)

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.ebo)
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, self.indices.nbytes, self.indices, GL_STATIC_DRAW)

        # 位置属性
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * self.vertices.itemsize, ctypes.c_void_p(0))
        glEnableVertexAttribArray(0)

        # 法线属性
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * self.vertices.itemsize,
                              ctypes.c_void_p(3 * self.vertices.itemsize))
        glEnableVertexAttribArray(1)

    def generate_cloud_callback(self, result):
        self.vertices, self.indices = result
        self.has_initialized = False

    def draw(self):
        if not self.has_initialized:
            self.update_mesh()
            self.has_initialized = True

        pos = np.array(
            [self.camera.transform.position[0], Constants.CLOUD_HEIGHT, self.camera.transform.position[2]])

        self.vertices, self.indices = JitFunc.generate_cloud_mesh_jit(
            np.array([pos[0] * 0.1, (pos[2] + self.cur_offset) * 0.1]),
            28
        )
        self.update_mesh()

        self.cur_offset += 3 * self.game_clock.delta_time

        glUseProgram(self.material.shader)
        glBindVertexArray(self.vao)

        # m_pos = np.array([0, pos[1], 0])
        model = pyrr.matrix44.create_from_translation(pos)
        scale = pyrr.matrix44.create_from_scale(np.array([12, 6, 12]))
        model = pyrr.matrix44.multiply(scale, model)
        glUniformMatrix4fv(self.material.model_loc, 1, GL_FALSE, model)

        glUniform2f(self.material.camera_pos_loc, self.camera.transform.position[0], self.camera.transform.position[2])

        # 绑定纹理
        glBindTexture(GL_TEXTURE_2D, self.material.texture)
        glDrawElements(GL_TRIANGLES, len(self.indices), GL_UNSIGNED_INT, None)


class SelectFrameMesh:
    def __init__(self, material):
        self.vao = glGenVertexArrays(1)
        self.vbo = glGenBuffers(1)
        self.ebo = glGenBuffers(1)
        self.vertices = Geometry.select_frame_vertices
        self.indices = Geometry.indices
        self.material = material
        self.has_initialized = False

    def update_mesh(self):
        glBindVertexArray(self.vao)

        glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
        glBufferData(GL_ARRAY_BUFFER, self.vertices.nbytes, self.vertices, GL_STATIC_DRAW)

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.ebo)
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, self.indices.nbytes, self.indices, GL_STATIC_DRAW)

        # 位置属性
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * self.vertices.itemsize, ctypes.c_void_p(0))
        glEnableVertexAttribArray(0)

        # 纹理属性
        glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * self.vertices.itemsize,
                              ctypes.c_void_p(3 * self.vertices.itemsize))
        glEnableVertexAttribArray(1)

    def draw(self, position):
        if not self.has_initialized:
            self.update_mesh()
            self.has_initialized = True
        glUseProgram(self.material.shader)
        glBindVertexArray(self.vao)

        glBindTexture(GL_TEXTURE_2D, self.material.texture)

        model = pyrr.matrix44.create_from_translation(np.array([position[0], position[1], position[2]]))
        glUniformMatrix4fv(self.material.model_loc, 1, GL_FALSE, model)

        glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, None)


class DestroyedBlockMesh:
    def __init__(self, material, position, block_type, game_clock, particle_manager, world):
        self.vao = glGenVertexArrays(1)
        self.vbo = glGenBuffers(1)
        self.ebo = glGenBuffers(1)
        shape = BlockRegistry[block_type].shape
        if shape == 1:
            self.vertices = Geometry.plant_vertices.copy()
            self.indices = Geometry.plant_indices.copy()
        else:
            self.vertices = Geometry.vertices.copy()
            self.indices = Geometry.indices.copy()
        self.material = material
        self.has_initialized = False
        self.lifetime = 1.0
        self.model = None
        self.indices_len = 0
        self.position = position
        self.y_pos = self.position[1]

        self.uv_pos = BlockRegistry[block_type].texture_id
        self.game_clock, self.particle_manager, self.world = game_clock, particle_manager, world

        if shape == 1:
            for i in range(0, 2):
                for j in range(0, 4):
                    self.vertices[i * 4 + j][3], self.vertices[i * 4 + j][4] = self.uv_pos[i][j][0], self.uv_pos[i][j][
                        1]
                    self.vertices[i * 4 + j][9] = BLOCK_DATA_SMOOTHNESS[block_type]
        else:
            for i in range(0, 6):
                for j in range(0, 4):
                    self.vertices[i * 4 + j][3], self.vertices[i * 4 + j][4] = self.uv_pos[i][j][0], self.uv_pos[i][j][
                        1]
                    self.vertices[i * 4 + j][9] = BLOCK_DATA_SMOOTHNESS[block_type]

    def update_mesh(self):
        glBindVertexArray(self.vao)
        glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
        glBufferData(GL_ARRAY_BUFFER, self.vertices.nbytes, self.vertices, GL_DYNAMIC_DRAW)
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.ebo)
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, self.indices.nbytes, self.indices, GL_DYNAMIC_DRAW)
        # 位置属性
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 10 * self.vertices.itemsize, ctypes.c_void_p(0))
        glEnableVertexAttribArray(0)
        # 纹理属性
        glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 10 * self.vertices.itemsize,
                              ctypes.c_void_p(3 * self.vertices.itemsize))
        glEnableVertexAttribArray(1)

        # 法向量属性
        glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 10 * self.vertices.itemsize,
                              ctypes.c_void_p(5 * self.vertices.itemsize))
        glEnableVertexAttribArray(2)

        # 环境遮蔽强度属性
        glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, 10 * self.vertices.itemsize,
                              ctypes.c_void_p(8 * self.vertices.itemsize))
        glEnableVertexAttribArray(3)

        # 光滑度属性
        glVertexAttribPointer(4, 1, GL_FLOAT, GL_FALSE, 10 * self.vertices.itemsize,
                              ctypes.c_void_p(9 * self.vertices.itemsize))
        glEnableVertexAttribArray(4)
        self.model = pyrr.matrix44.create_from_translation(
            np.array([self.position[0], self.position[1], self.position[2]]))

        self.indices_len = len(self.indices)

    # 定义插值函数
    def smooth_interpolate(self, x):
        """
        在[0.4, 1]区间内的平滑插值函数
        满足: f(0.4)=0.4, f(1)=1, f'(0.4)=1, f'(1)=1
        """
        # 使用归一化的x值到[0,1]区间
        t = (x - 0.4) / 0.6

        # 三次多项式系数
        a = -1
        b = 1
        c = 1
        d = 0

        # 计算插值
        y = a * t ** 3 + b * t ** 2 + c * t + d

        # 映射回原始范围
        return 0.4 + y * 0.6

    def get_model(self, progress):
        div = 0.4
        if div <= progress:
            model_translation = pyrr.matrix44.create_from_translation(
                np.array(self.position)
            )
            interpolate = self.smooth_interpolate(progress)
            model_scale = pyrr.matrix44.create_from_scale(
                np.array([interpolate, interpolate, interpolate])
            )
            final_model = pyrr.matrix44.multiply(model_scale, model_translation)

        elif self.world.get_block(np.array([self.position[0], self.y_pos + div * 0.75, self.position[2]])) == 0:
            self.y_pos -= 8 * self.game_clock.delta_time
            model_translation = pyrr.matrix44.create_from_translation([self.position[0], self.y_pos, self.position[2]])

            model_scale = pyrr.matrix44.create_from_scale(
                np.array([div, div, div])
            )
            final_model = pyrr.matrix44.multiply(model_scale, model_translation)
        else:
            model_translation = pyrr.matrix44.create_from_translation(
                np.array([self.position[0], self.y_pos, self.position[2]]), dtype=np.float32)

            model_scale = pyrr.matrix44.create_from_scale(
                np.array([div, div, div])
            )
            final_model = pyrr.matrix44.multiply(model_scale, model_translation)

        return final_model

    def draw(self, player_position):
        if not self.has_initialized:
            self.update_mesh()
            self.has_initialized = True

        if self.lifetime < -5.0:
            self.particle_manager.particles.remove(self)
            return
        self.lifetime -= 2 * self.game_clock.delta_time

        final_model = self.get_model(self.lifetime)

        glBindVertexArray(self.vao)
        glUniformMatrix4fv(self.material.model_loc, 1, GL_FALSE, final_model)
        glDrawElements(GL_TRIANGLES, self.indices_len, GL_UNSIGNED_INT, None)


class UIMesh:
    def __init__(self,
                 image_path='Resources/UI/test.jpg',
                 screen_position=(0.0, 0.0),
                 rotation=0.0,
                 pixel_size=(64.0, 64.0),
                 tint=(1.0, 1.0, 1.0, 1.0),
                 enabled=True,
                 child_mesh=None,
                 texture_uv=None,
                 ui_manager=None,
                 vert_shader=Geometry.ui_vertex_shader,
                 frag_shader=Geometry.ui_fragment_shader):
        self.image_path = image_path
        if self.image_path is None:
            self.material = Materials.UIMaterial(vert_shader, frag_shader, 'Resources/UI/test.jpg')
        else:
            self.material = Materials.UIMaterial(vert_shader, frag_shader, self.image_path)

        self.vao = glGenVertexArrays(1)
        self.vbo = glGenBuffers(1)
        self.ebo = glGenBuffers(1)
        self.vertices = Geometry.ui_vertices
        self.indices = Geometry.ui_indices
        self.screen_position = screen_position
        self.rotation = rotation
        self.pixel_size = pixel_size
        self.tint = tint
        self.enabled = enabled
        self.ui_manager = ui_manager

        self.child_mesh = child_mesh
        self.has_copied = False
        self.parent_mesh = None

        self.has_initialized = False

        if texture_uv is not None:
            self.vertices = Geometry.ui_vertices.copy()
            self.has_copied = True
            for i in range(0, 4):
                self.vertices[i * 5 + 3], self.vertices[i * 5 + 4] = texture_uv[i][0], texture_uv[i][1]

        if self.child_mesh is not None:
            for mesh in self.child_mesh:
                if mesh is not None:
                    mesh.parent_mesh = self

    def set_enabled(self, value):
        self.enabled = value

    def change_texture_uv(self, texture_uv):
        if not self.has_copied:
            self.vertices = Geometry.ui_vertices.copy()
            self.has_copied = True
        for i in range(0, 4):
            self.vertices[i * 5 + 3], self.vertices[i * 5 + 4] = texture_uv[i][0], texture_uv[i][1]
        self.update_mesh()

    def set_tint(self, tint):
        self.tint = tint
        self.update_mesh()

    def update_mesh(self):
        glBindVertexArray(self.vao)

        glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
        glBufferData(GL_ARRAY_BUFFER, self.vertices.nbytes, self.vertices, GL_DYNAMIC_DRAW)

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.ebo)
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, self.indices.nbytes, self.indices, GL_DYNAMIC_DRAW)

        # 位置属性
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * self.vertices.itemsize, ctypes.c_void_p(0))
        glEnableVertexAttribArray(0)

        # 纹理坐标属性
        glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * self.vertices.itemsize,
                              ctypes.c_void_p(3 * self.vertices.itemsize))
        glEnableVertexAttribArray(1)

        glUseProgram(self.material.shader)

        glUniform4f(self.material.tint_loc, self.tint[0], self.tint[1], self.tint[2], self.tint[3])

    def draw(self):
        if not self.enabled:
            return

        # 绘制子UI
        if self.child_mesh is not None:
            for mesh in self.child_mesh:
                if mesh is not None:
                    mesh.draw()
        if not self.has_initialized:
            self.update_mesh()
            self.has_initialized = True

        if self.image_path is None:
            return

        glBindVertexArray(self.vao)

        # 使用着色器程序
        glUseProgram(self.material.shader)

        # 绑定纹理
        glBindTexture(GL_TEXTURE_2D, self.material.texture)

        # 设置uniform
        if self.parent_mesh is not None:
            model_translate = pyrr.matrix44.create_from_translation(
                np.array([(self.screen_position[0] + self.parent_mesh.screen_position[0]) * 2,
                          (self.screen_position[1] + self.parent_mesh.screen_position[1]) * 2, 0])
            )
        else:
            model_translate = pyrr.matrix44.create_from_translation(
                np.array([self.screen_position[0] * 2,
                          self.screen_position[1] * 2, 0])
            )

        model_scale = pyrr.matrix44.create_from_scale(
            np.array([self.pixel_size[0], self.pixel_size[1], 0.0])
        )
        model_rot = pyrr.matrix44.create_from_z_rotation(np.radians(self.rotation))
        # 3. 按正确顺序组合矩阵：缩放 → 旋转 → 平移
        model = pyrr.matrix44.multiply(model_scale, model_rot)  # 先缩放再旋转
        model = pyrr.matrix44.multiply(model, model_translate)  # 再平移
        glUniformMatrix4fv(self.material.transform_loc, 1, GL_FALSE, model)

        # 绘制
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, None)


ALPHA_GAP = [
    0.625,  #
    0.25,  # !
    0.75,  # "
    0.875,  ##
    0.875,  # $
    0.875,  # %
    0.875,  # &
    0.5,  # '
    0.75,  # (
    0.75,  # )
    0.75,  # *
    0.875,  # +
    0.25,  # ,
    0.875,  # -
    0.25,  # .
    0.875,  # /
    0.875,  # 0
    0.875,  # 1
    0.875,  # 2
    0.875,  # 3
    0.875,  # 4
    0.875,  # 5
    0.875,  # 6
    0.875,  # 7
    0.875,  # 8
    0.875,  # 9
    0.25,  #:
    0.25,  # ;
    0.75,  # <
    0.875,  # =
    0.75,  # >
    0.875,  # ?
    1.0,  # @
    0.875,  # A
    0.875,  # B
    0.875,  # C
    0.875,  # D
    0.875,  # E
    0.875,  # F
    0.875,  # G
    0.875,  # H
    0.625,  # I
    0.875,  # J
    0.875,  # K
    0.875,  # L
    0.875,  # M
    0.875,  # N
    0.875,  # O
    0.875,  # P
    0.875,  # Q
    0.875,  # R
    0.875,  # S
    0.875,  # T
    0.875,  # U
    0.875,  # V
    0.875,  # W
    0.875,  # X
    0.875,  # Y
    0.875,  # Z
    0.625,  # [
    0.875,  # \
    0.625,  # ]
    0.875,  # ^
    0.875,  # _
    0.125,  # `
    0.875,  # a
    0.875,  # b
    0.875,  # c
    0.875,  # d
    0.875,  # e
    0.75,  # f
    0.875,  # g
    0.875,  # h
    0.25,  # i
    0.875,  # j
    0.75,  # k
    0.5,  # l
    0.875,  # m
    0.875,  # n
    0.875,  # o
    0.875,  # p
    0.875,  # q
    0.875,  # r
    0.875,  # s
    0.625,  # t
    0.875,  # u
    0.875,  # v
    0.875,  # w
    0.875,  # x
    0.875,  # y
    0.875,  # z
    0.75,  # {
    0.25,  # |
    0.75,  # }
    1.0,  # ~
    0.125,  # 
]


class TextMesh(UIMesh):
    def __init__(self,
                 text,
                 screen_position=(0.0, 0.0),
                 rotation=0.0,
                 tint=(1.0, 1.0, 1.0, 1.0),
                 font_scale=1.0,
                 padding_mode=1,
                 enabled=True,
                 child_mesh=None,
                 ui_manager=None):
        super().__init__(image_path='Resources/Texts/character_atlas_transparent.png',
                         screen_position=screen_position,
                         rotation=rotation,
                         pixel_size=(53.0, 53.0),
                         tint=tint,
                         enabled=enabled,
                         child_mesh=child_mesh,
                         ui_manager=ui_manager,
                         vert_shader=Geometry.text_vertex_shader,
                         frag_shader=Geometry.text_fragment_shader
                         )
        self.text = text
        self.padding_mode = padding_mode
        self.font_scale = font_scale
        self.gen_mesh_from_text(self.text)

    def gen_mesh_from_text(self, text):
        str_len = len(text)
        vertices_list, indices_list = [], []  # 添加数据使用列表操作更快
        index = 0
        if self.padding_mode == 0:  # 左对齐
            x_offset = -(str_len - 1) * (self.pixel_size[0] * 24 / (Constants.DISPLAY[0]))
        elif self.padding_mode == 1:  # 中对齐
            x_offset = -str_len * 0.375 * (self.pixel_size[0] * 24 / (Constants.DISPLAY[0]))
        else:  # 右对齐
            x_offset = str_len * 0.5 * (self.pixel_size[0] * 24 / (Constants.DISPLAY[0]))

        x_offset = x_offset * Constants.DISPLAY[0] / Constants.ORIGIN_DISPLAY[0]
        for char in text:
            uv_coords = Utils.get_texture_list(ord(char) - 32)

            vertices_list.extend([
                (-0.5 + x_offset, -0.5, 0.0, uv_coords[0][0], uv_coords[0][1]),
                (0.5 + x_offset, -0.5, 0.0, uv_coords[1][0], uv_coords[1][1]),
                (0.5 + x_offset, 0.5, 0.0, uv_coords[2][0], uv_coords[2][1]),
                (-0.5 + x_offset, 0.5, 0.0, uv_coords[3][0], uv_coords[3][1])
            ])
            indices_list.extend([index, index + 1, index + 3, index + 1, index + 2, index + 3])
            index += 4
            offset = (self.pixel_size[0] * 24 / (Constants.DISPLAY[0]))
            mul = ALPHA_GAP[ord(char) - 32]
            x_offset += offset * Constants.DISPLAY[0] * mul * 1.1 / Constants.ORIGIN_DISPLAY[0]
        self.vertices = np.array(vertices_list, dtype=np.float32)
        self.indices = np.array(indices_list, dtype=np.uint32)

    def set_text(self, text):
        self.text = text
        self.gen_mesh_from_text(self.text)
        self.update_mesh()

    def draw(self):
        if not self.enabled:
            return

        # 绘制子UI
        if self.child_mesh is not None:
            for mesh in self.child_mesh:
                if mesh is not None:
                    mesh.draw()
        if not self.has_initialized:
            self.update_mesh()
            self.has_initialized = True
        glBindVertexArray(self.vao)

        # 使用着色器程序
        glUseProgram(self.material.shader)

        # 绑定纹理
        glBindTexture(GL_TEXTURE_2D, self.material.texture)

        # 设置uniform
        if self.parent_mesh is not None:
            model_translate = pyrr.matrix44.create_from_translation(
                np.array([(self.screen_position[0] + self.parent_mesh.screen_position[0]) * 2,
                          (self.screen_position[1] + self.parent_mesh.screen_position[1]) * 2, -0.6])
            )
        else:
            model_translate = pyrr.matrix44.create_from_translation(
                np.array([self.screen_position[0] * 2,
                          self.screen_position[1] * 2, -0.6])
            )

        model_scale = pyrr.matrix44.create_from_scale(
            np.array([self.pixel_size[0] * self.font_scale,
                      self.pixel_size[1] * self.font_scale * self.ui_manager.current_aspect, 1])
        )
        model_rot = pyrr.matrix44.create_from_z_rotation(np.radians(self.rotation))

        # 3. 按正确顺序组合矩阵：缩放 → 旋转 → 平移
        model = pyrr.matrix44.multiply(model_rot, model_scale)  # 先缩放再旋转
        model = pyrr.matrix44.multiply(model, model_translate)  # 再平移

        # model = pyrr.matrix44.multiply(model_scale, model_translate)
        glUniformMatrix4fv(self.material.transform_loc, 1, GL_FALSE, model)

        # 绘制
        # glBindVertexArray(self.vao)
        glDrawElements(GL_TRIANGLES, self.indices.size, GL_UNSIGNED_INT, None)


class UIBlockMesh(UIMesh):
    def __init__(self,
                 block_type=12,
                 screen_position=(0.0, -0.25),
                 rotation=(-45, 45, 0),
                 rot_speed=0,
                 tint=(1.0, 1.0, 1.0, 1.0),
                 scale=1.0,
                 z_pos=-0.5,
                 enabled=True,
                 child_mesh=None,
                 ui_manager=None,
                 game_clock=None
                 ):
        super().__init__(image_path='Resources/Blocks/blocks2.png',
                         screen_position=screen_position,
                         rotation=0,
                         pixel_size=(53.0, 53.0),
                         tint=tint,
                         enabled=enabled,
                         child_mesh=child_mesh,
                         ui_manager=ui_manager,
                         vert_shader=Geometry.ui_three_vertex_shader,
                         frag_shader=Geometry.ui_three_fragment_shader
                         )
        self.rot_speed = rot_speed
        self.scale = scale
        self.z_pos = z_pos
        self.game_clock = game_clock
        self.rotation = [*rotation]
        self.origin_rotation = rotation
        self.animation_progress = 1.0

        self.block_vertices = Geometry.vertices.copy()
        # 立方体的索引数据
        self.block_indices = Geometry.indices
        self.plant_vertices = Geometry.plant_vertices.copy()
        self.plant_indices = Geometry.plant_indices

        self.vertices = self.block_vertices
        # 立方体的索引数据
        self.indices = self.block_indices
        self.set_block_texture(block_type)

    def set_block_texture(self, block_type):
        block_kind = BlockRegistry.get(block_type).block_kind
        block_texture = BlockRegistry.get(block_type).texture_id
        if block_kind == BlockKinds.Flowered:
            self.vertices = self.plant_vertices
            self.indices = self.plant_indices
            for i in range(0, 8):
                self.vertices[i][3], self.vertices[i][4] = block_texture[0][i % 4][0], block_texture[0][i % 4][1]
        else:
            self.vertices = self.block_vertices
            self.indices = self.block_indices
            for f in range(0, 6):
                self.vertices[f * 4 + 0][3] = block_texture[f][0][0]
                self.vertices[f * 4 + 0][4] = block_texture[f][0][1]

                self.vertices[f * 4 + 1][3] = block_texture[f][1][0]
                self.vertices[f * 4 + 1][4] = block_texture[f][1][1]

                self.vertices[f * 4 + 2][3] = block_texture[f][2][0]
                self.vertices[f * 4 + 2][4] = block_texture[f][2][1]

                self.vertices[f * 4 + 3][3] = block_texture[f][3][0]
                self.vertices[f * 4 + 3][4] = block_texture[f][3][1]

        self.update_mesh()

    def swing(self):
        self.animation_progress = 0.0

    def swing_animation(self, progress):
        progress = math.sin(progress * math.pi)
        target_rotation = (self.origin_rotation[0] + 45, self.origin_rotation[1] + 15, self.origin_rotation[2] - 15)
        self.rotation[0], self.rotation[1], self.rotation[2] = (1 - progress) * self.origin_rotation[0] + progress * \
                                                               target_rotation[0], (1 - progress) * \
                                                               self.origin_rotation[1] + progress * target_rotation[
                                                                   1], (1 - progress) * self.origin_rotation[
                                                                   2] + progress * target_rotation[2]

    def update_mesh(self):
        glBindVertexArray(self.vao)

        glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
        glBufferData(GL_ARRAY_BUFFER, self.vertices.nbytes, self.vertices, GL_DYNAMIC_DRAW)

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.ebo)
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, self.indices.nbytes, self.indices, GL_DYNAMIC_DRAW)

        # 位置属性
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 10 * self.vertices.itemsize, ctypes.c_void_p(0))
        glEnableVertexAttribArray(0)

        # 纹理坐标属性
        glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 10 * self.vertices.itemsize,
                              ctypes.c_void_p(3 * self.vertices.itemsize))
        glEnableVertexAttribArray(1)

        glUseProgram(self.material.shader)

        glUniform4f(self.material.tint_loc, self.tint[0], self.tint[1], self.tint[2], self.tint[3])

    def draw(self):
        if not self.enabled:
            return

        if self.animation_progress < 1.0:
            self.swing_animation(self.animation_progress)
            self.animation_progress += 3 * self.game_clock.delta_time

        # 绘制子UI
        if self.child_mesh is not None:
            for mesh in self.child_mesh:
                if mesh is not None:
                    mesh.draw()
        if not self.has_initialized:
            self.update_mesh()
            self.has_initialized = True

        glBindVertexArray(self.vao)

        # 使用着色器程序
        glUseProgram(self.material.shader)

        # 绑定纹理
        glBindTexture(GL_TEXTURE_2D, self.material.texture)

        # 设置uniform
        if self.parent_mesh is not None:
            model_translate = pyrr.matrix44.create_from_translation(
                np.array([(self.screen_position[0] + self.parent_mesh.screen_position[0]) * 2,
                          (self.screen_position[1] + self.parent_mesh.screen_position[1]) * 2, self.z_pos])
            )
        else:
            model_translate = pyrr.matrix44.create_from_translation(
                np.array([self.screen_position[0] * 2,
                          self.screen_position[1] * 2, self.z_pos])
            )

        model_scale = pyrr.matrix44.create_from_scale(
            np.array([self.scale, self.scale * 2.0, self.scale])
        )
        if self.game_clock is not None:
            self.rotation[1] += self.rot_speed * self.game_clock.delta_time
        model_rot = pyrr.matrix44.create_from_x_rotation(np.radians(self.rotation[0]))
        model_rot = pyrr.matrix44.multiply(pyrr.matrix44.create_from_y_rotation(np.radians(self.rotation[1]),
                                                                                ), model_rot)
        model_rot = pyrr.matrix44.multiply(pyrr.matrix44.create_from_z_rotation(np.radians(self.rotation[2]),
                                                                                ), model_rot)
        # 3. 按正确顺序组合矩阵：缩放 → 旋转 → 平移
        model = pyrr.matrix44.multiply(model_scale, model_rot)  # 先缩放再旋转
        model = pyrr.matrix44.multiply(model, model_translate)  # 再平移
        glUniformMatrix4fv(self.material.transform_loc, 1, GL_FALSE, model)

        # 绘制
        glDrawElements(GL_TRIANGLES, len(self.indices), GL_UNSIGNED_INT, None)
